package com.hxx.sbcommon.common.io.http;

import com.hxx.sbcommon.common.io.http.config.HttpClientConstants;
import com.hxx.sbcommon.common.io.http.tools.HttpClientTools;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.pool.PoolStats;
import org.apache.http.util.CharArrayBuffer;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.*;
import java.security.KeyStore;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * apache-http驱动
 * header信息，每次都需要重新写入请求
 * * <p>
 * * BA认证：
 * * urlParameters.add(new BasicNameValuePair("username", "abc"));
 * * urlParameters.add(new BasicNameValuePair("password", "123"));
 * * <p>
 *
 * @Author: huoxuxu
 * @Description:
 * @Date: 2023-06-29 12:57:35
 **/
@Slf4j
public class ApacheHttpClientSimpleUseful {

    private static final CloseableHttpClient HTTP_CLIENT;

    static {
        HTTP_CLIENT = HttpClientTools.createHttpClient(HttpClientConstants.maxConnectionSize,
                HttpClientConstants.maxPerRouteSize,
                HttpClientConstants.maxIdleSecond,
                HttpClientConstants.DEFAULT_CONNECTION_REQUEST_TIMEOUT,
                HttpClientConstants.DEFAULT_CONNECTION_TIMEOUT,
                HttpClientConstants.DEFAULT_SOCKET_TIMEOUT,
                null, 0);
    }

    private ApacheHttpClientSimpleUseful() {
    }

    /**
     * 获取ApacheHttpClient
     *
     * @return
     */
    public static CloseableHttpClient getHttpClient() {
        return HTTP_CLIENT;
    }

    /**
     * 发起 GET 请求
     *
     * @param url
     * @param header
     * @param params
     * @return
     * @throws IOException
     */
    public static String sendGet(String url, Map<String, String> header, Map<String, String> params) throws IOException {
        try (HttpApiResp resp = sendGetResp(url, header, params)) {
            log.debug("sendHttpGet-resp {} statusCode:{} contentLength:{}", url, resp.getStatusCode(), resp.getContentLength());

            return resp.readString();
        }
    }

    /**
     * 发起 POST 请求
     *
     * @param url
     * @param header
     * @param json
     * @return
     * @throws IOException
     */
    public static String sendPostJson(String url, Map<String, String> header, String json) throws IOException {
        try (HttpApiResp resp = sendPostResp(url, header, json, HttpClientConstants.JSON_UTF_8)) {
            log.debug("sendJsonPost-resp {} statusCode:{} contentLength:{}", url, resp.getStatusCode(), resp.getContentLength());

            return resp.readString();
        }
    }

    /**
     * 发送Post请求
     *
     * @param url
     * @param header
     * @param formBody
     * @return
     * @throws IOException
     */
    public static String sendPostUrlEncoded(String url, Map<String, String> header, String formBody) throws IOException {
        try (HttpApiResp resp = sendPostResp(url, header, formBody, HttpClientConstants.URL_ENCODED_UTF_8)) {
            log.debug("sendPostUrlEncoded-resp {} statusCode:{} contentLength:{}", url, resp.getStatusCode(), resp.getContentLength());

            return resp.readString();
        }
    }

    /**
     * 发起 GET 请求
     *
     * @param url
     * @param header
     * @param params
     * @return
     * @throws IOException
     */
    public static HttpApiResp sendGetResp(String url, Map<String, String> header, Map<String, String> params) throws IOException {
        if (params != null) {
            // 追加到url中
            url = appendQueryString(url, params);
        }
//        RequestConfig requestConfig = getRequestConfig();
        HttpGet httpGet = new HttpGet(url);
//        httpGet.setConfig(requestConfig);
        httpGet.setHeader("Content-type", HttpClientTools.getContentType(HttpClientConstants.CONTENT_VALUE_FORM, HttpClientConstants.DEFAULT_CHAR_SET));
        log.debug("sendHttpGet url:{}", url);

        return HttpClientTools.execute(HTTP_CLIENT, url, httpGet, header);
    }

    /**
     * 发起Post请求
     *
     * @param url
     * @param header
     * @param formBody
     * @param contentType
     * @return
     * @throws IOException
     */
    public static HttpApiResp sendPostResp(String url, Map<String, String> header, String formBody, ContentType contentType) throws IOException {
        HttpPost httpPost = new HttpPost(url);
//        httpPost.setConfig(requestConfig);
        StringEntity stringEntity = new StringEntity(formBody, contentType);
        httpPost.setEntity(stringEntity);
        log.debug("sendJsonPost url={}, formBody={}", url, formBody);
        // 发起请求
        return HttpClientTools.execute(HTTP_CLIENT, url, httpPost, header);
    }

    // 处理QueryString
    private static String appendQueryString(String url, Map<String, String> params) throws IOException {
        if (params.isEmpty()) return url;

        List<NameValuePair> urlParams = new ArrayList<>(params.size());
        for (Map.Entry<String, String> entry : params.entrySet()) {
            String value = entry.getValue();
            if (value == null) continue;

            urlParams.add(new BasicNameValuePair(entry.getKey(), value));
        }

        String urlQs = EntityUtils.toString(new UrlEncodedFormEntity(urlParams, HttpClientConstants.DEFAULT_CHAR_SET));
        String appChar = url.contains("?") ? "" : "?";
        url = url + appChar + urlQs;
        return url;
    }

    /**
     * Http响应对象
     */
    @Data
    public static class HttpApiResp implements Closeable {

        private String url;
        private CloseableHttpResponse response;
        private HttpEntity entity;

        /**
         * 是否请求成功，httpstatuscode >=200 && <300
         */
        private boolean ok;

        /**
         * 状态码
         */
        private int statusCode;
        /**
         * 内容长度
         */
        private long contentLength;

        /**
         * 是否自动关闭响应
         */
        private boolean autoCloseResponse;

        public HttpApiResp() {
        }

        /**
         * @param response
         * @param autoCloseResponse 是否自动关闭响应
         * @throws IOException
         */
        public HttpApiResp(String url, CloseableHttpResponse response, boolean autoCloseResponse) {
            this.url = url;
            this.response = response;
            this.autoCloseResponse = autoCloseResponse;
            // 获取响应体
            this.entity = response.getEntity();

            int statusCode = response.getStatusLine().getStatusCode();
            this.statusCode = statusCode;
            this.ok = statusCode >= 200 && statusCode < 300;
            this.contentLength = entity.getContentLength();
        }

        @Override
        public void close() throws IOException {
            if (autoCloseResponse && this.response != null) {
                this.response.close();
            }
        }

        /**
         * 处理响应，非200的响应会抛出异常
         *
         * @return
         * @throws IOException
         */
        public String readString() throws IOException {
            if (this.statusCode != HttpStatus.SC_OK) {
                if (this.contentLength > 0 && log.isDebugEnabled()) {
                    log.debug("Http请求失败，{} statusCode:{} contentLength:{} {}", this.getUrl(), this.getStatusCode(), this.getContentLength(), EntityUtils.toString(this.getEntity()));
                } else {
                    log.warn("Http请求失败，{} statusCode:{} contentLength:{}", this.getUrl(), this.getStatusCode(), this.getContentLength());
                }
                throw new ClientProtocolException("非预期的Http响应码：" + this.statusCode + " 请求地址：" + this.getUrl());
            }

            return EntityUtils.toString(this.getEntity());
        }

        private String readStringCustom() throws IOException {
            int capacity = (int) this.getContentLength();
            if (capacity < 0) capacity = 4096;

            try (InputStream inStream = this.entity.getContent()) {
                Reader reader = new InputStreamReader(inStream, HttpClientConstants.DEFAULT_CHAR_SET);
                CharArrayBuffer buffer = new CharArrayBuffer(capacity);
                char[] tmp = new char[16 * 1024];

                int len;
                while ((len = reader.read(tmp)) != -1) {
                    buffer.append(tmp, 0, len);
                }

                return buffer.toString();
            }
        }
    }
}
